﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using App1AppHandler;
using System.IO;
using System.Xml;
using System.Net.Sockets;
using Reborn_WorldServer;

namespace Reborn_Server.App1AppHandler
{
    public class TradeData
    {
        private Character RequestCharacter = null;
        private Character AcceptingCharacter = null;

        public AppHandler2 GameHandler = null;

        private bool _RequesterHasAccepted = false;
        private bool RequesterHasAccepted
        {
            get { return _RequesterHasAccepted; }
            set
            {
                _RequesterHasAccepted = value;

                sendAcceptStatus(AcceptingCharacter, RequestCharacter.CreatureID, value);
                sendAcceptStatus(RequestCharacter, RequestCharacter.CreatureID, value);
            }
        }

        private bool _AcceptingHasAccepted = false;
        private bool AcceptingHasAccepted
        {
            get { return _AcceptingHasAccepted; }
            set
            {
                _AcceptingHasAccepted = value;

                sendAcceptStatus(AcceptingCharacter, AcceptingCharacter.CreatureID, value);
                sendAcceptStatus(RequestCharacter, AcceptingCharacter.CreatureID, value);
            }
        }
        private void sendAcceptStatus(Character toCharacter, long creatureID, bool status)
        {
            string result = "";

            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("traderacceptstatus");
            xmldoc.WriteAttributeString("TraderCreatureID", creatureID.ToString());
            xmldoc.WriteAttributeString("AcceptStatus", status.ToString());
            xmldoc.WriteEndElement();

            xmldoc.Flush();
            xmldoc.Close();
            stringWriter.Flush();
            result = stringWriter.ToString();

            try
            {
                toCharacter.ClientSocket.socket.BeginSend(result);
            }
            catch { }
        }


        private List<Loot> RequesterTradeItems = new List<Loot>();
        private List<Loot> AcceptingTradeItems = new List<Loot>();

        private int RequesterCoins = 0;
        private int AcceptingCoins = 0;

        public TradeData(AppHandler2 gameHandler)
        {
            GameHandler = gameHandler;
        }

        public void MakeRequest(Character requestChar, long acceptCreatureID)
        {
            LockItem lockItem = GameHandler.GetLockItem(requestChar.MapID, requestChar.Instance);
            lock (lockItem.LockObj)
            {
                if (requestChar.tradeData != null)
                    return;

                Map CurrentMap = GameHandler.GetMap(requestChar.MapID, requestChar.Instance);
                MapItem CurrentMapItem = null;
                foreach (MapItem mapitem in CurrentMap.MapItems)
                {
                    if (mapitem.MapX == requestChar.MapX &&  mapitem.MapY == requestChar.MapY)
                    {
                        CurrentMapItem = mapitem;
                        break;
                    }
                }

                foreach (CreatureBase creatureBase in CurrentMapItem.CreaturesBases)
                {
                    if (creatureBase.CreatureID == acceptCreatureID && creatureBase is Character)
                    {
                        AcceptingCharacter = (Character)creatureBase;

                        if (AcceptingCharacter.tradeData != null)
                        {
                            Message message = new Message();
                            message.CreatureID = requestChar.CreatureID;
                            message.Type = "server";
                            message.Text = "Player is trading with someone else";

                            requestChar.AddMessage(message);
                            requestChar.PlaySound("Ding", requestChar.MapX, requestChar.MapY);

                            sendCancelation(requestChar);

                            return;
                        }

                        AcceptingCharacter.tradeData = this;
                        break;
                    }
                }

                if (AcceptingCharacter == null)
                {
                    Message message = new Message();
                    message.CreatureID = requestChar.CreatureID;
                    message.Type = "server";
                    message.Text = "Cannot find player";

                    requestChar.AddMessage(message);
                    requestChar.PlaySound("Ding", requestChar.MapX, requestChar.MapY);

                    sendCancelation(requestChar);

                    return;
                }
                RequestCharacter = requestChar;
                requestChar.tradeData = this;
            }

            sendRequest();
        }
        private void sendRequest()
        {
            string result = "";

            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("traderequest");
            xmldoc.WriteAttributeString("Requester", RequestCharacter.Name);
            xmldoc.WriteEndElement();

            xmldoc.Flush();
            xmldoc.Close();
            stringWriter.Flush();
            result = stringWriter.ToString();

            try
            {
                AcceptingCharacter.ClientSocket.socket.BeginSend(result);
            }
            catch { }
        }
        public void CancelTrade(Character character, bool isBusy)
        {
            lock (this)
            {
                if (character != RequestCharacter && character != AcceptingCharacter)
                    return;

                sendCancelation(AcceptingCharacter);
                sendCancelation(RequestCharacter);

                if (character == RequestCharacter)
                {
                    Message message = new Message();
                    message.CreatureID = RequestCharacter.CreatureID;
                    message.Type = "server";

                    if (isBusy == true)
                    {
                        message.Text = RequestCharacter.Name + " is busy";
                        AcceptingCharacter.PlaySound("Ding", AcceptingCharacter.MapX, AcceptingCharacter.MapY);
                    }
                    else
                        message.Text = RequestCharacter.Name + " canceled the trade";

                    AcceptingCharacter.AddMessage(message);
                }
                else if (character == AcceptingCharacter)
                {
                    Message message = new Message();
                    message.CreatureID = RequestCharacter.CreatureID;
                    message.Type = "server";

                    if (isBusy == true)
                    {
                        message.Text = AcceptingCharacter.Name + " is busy";
                        RequestCharacter.PlaySound("Ding", RequestCharacter.MapX, RequestCharacter.MapY);
                    }
                    else
                        message.Text = AcceptingCharacter.Name + " canceled the trade";

                    RequestCharacter.AddMessage(message);
                }
            }
        }
        public void AcceptRequest()
        {
            lock (this)
            {
                openTradeWindow(AcceptingCharacter);
                openTradeWindow(RequestCharacter);
            }
        }
        private void sendCancelation(Character character)
        {
            character.tradeData = null;

            try
            {
                string result = "";

                StringWriter stringWriter = new StringWriter();
                XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

                xmldoc.WriteStartElement("tradecancel");
                xmldoc.WriteEndElement();

                xmldoc.Flush();
                xmldoc.Close();
                stringWriter.Flush();
                result = stringWriter.ToString();

                character.ClientSocket.socket.BeginSend(result);
            }
            catch { }
        }

        public void AcceptTradeOffer(Character character)
        {
            if (character == RequestCharacter)
                RequesterHasAccepted = true;
            else if (character == AcceptingCharacter)
                AcceptingHasAccepted = true;

            if (RequesterHasAccepted == true && AcceptingHasAccepted == true)
            {
                //Close trade windows
                sendCancelation(AcceptingCharacter);
                sendCancelation(RequestCharacter);

                foreach (Loot item in AcceptingTradeItems)
                {
                    if (item.Lore == true && RequestCharacter.hasItem(item.ID))
                    {
                        Message msg = new Message();
                        msg.CreatureID = RequestCharacter.CreatureID;
                        msg.Type = "server";
                        msg.Text = "Trade failed. " + RequestCharacter.Name + " may not carry any more " + item.Name; ;

                        RequestCharacter.AddMessage(msg);
                        AcceptingCharacter.AddMessage(msg);
                        RequestCharacter.PlaySound("Ding", RequestCharacter.MapX, RequestCharacter.MapY);
                        AcceptingCharacter.PlaySound("Ding", AcceptingCharacter.MapX, AcceptingCharacter.MapY);

                        return;
                    }
                }
                foreach (Loot item in RequesterTradeItems)
                {
                    if (item.Lore == true && AcceptingCharacter.hasItem(item.ID))
                    {
                        Message msg = new Message();
                        msg.CreatureID = AcceptingCharacter.CreatureID;
                        msg.Type = "server";
                        msg.Text = "Trade failed. " + AcceptingCharacter.Name + " may not carry any more " + item.Name; ;

                        RequestCharacter.AddMessage(msg);
                        AcceptingCharacter.AddMessage(msg);
                        RequestCharacter.PlaySound("Ding", RequestCharacter.MapX, RequestCharacter.MapY);
                        AcceptingCharacter.PlaySound("Ding", AcceptingCharacter.MapX, AcceptingCharacter.MapY);

                        return;
                    }
                }

                //Check if both parties have enough inventory space (pre removal of trade items)
                bool RequesterHasInventory = RequestCharacter.HasInventoryFor(AcceptingTradeItems);
                bool AccepterHasInventory = AcceptingCharacter.HasInventoryFor(RequesterTradeItems);

                //Also check to make sure they are not offering more coins than they carry (should never happen)
                if (RequesterHasInventory == false || AccepterHasInventory == false || RequesterCoins > RequestCharacter.Coins || AcceptingCoins > AcceptingCharacter.Coins)
                {
                    if (RequesterHasInventory == false)
                    {
                        Message msg = new Message();
                        msg.CreatureID = RequestCharacter.CreatureID;
                        msg.Type = "server";
                        msg.Text = "Trade failed. " + RequestCharacter.Name + " does not have enough inventory room";

                        RequestCharacter.AddMessage(msg);
                        AcceptingCharacter.AddMessage(msg);
                    }
                    if (AccepterHasInventory == false)
                    {
                        Message msg = new Message();
                        msg.CreatureID = RequestCharacter.CreatureID;
                        msg.Type = "server";
                        msg.Text = "Trade failed. " + AcceptingCharacter.Name + " does not have enough inventory room";

                        RequestCharacter.AddMessage(msg);
                        AcceptingCharacter.AddMessage(msg);
                    }
                    if (RequesterCoins > RequestCharacter.Coins)
                    {
                        Message msg = new Message();
                        msg.CreatureID = RequestCharacter.CreatureID;
                        msg.Type = "server";
                        msg.Text = "Trade failed. " + RequestCharacter.Name + " tried to offer more coins than they have available";

                        RequestCharacter.AddMessage(msg);
                        AcceptingCharacter.AddMessage(msg);
                    }
                    if (AcceptingCoins > AcceptingCharacter.Coins)
                    {
                        Message msg = new Message();
                        msg.CreatureID = RequestCharacter.CreatureID;
                        msg.Type = "server";
                        msg.Text = "Trade failed. " + AcceptingCharacter.Name + " tried to offer more coins than they have available";

                        RequestCharacter.AddMessage(msg);
                        AcceptingCharacter.AddMessage(msg);
                    }

                    RequestCharacter.PlaySound("Ding", RequestCharacter.MapX, RequestCharacter.MapY);
                    AcceptingCharacter.PlaySound("Ding", AcceptingCharacter.MapX, AcceptingCharacter.MapY);

                    return;
                }

                bool requestCharBagUpdate1;
                bool requestCharBeltUpdate1;
                bool requestCharCharUpdate1;
                
                bool requestCharBagUpdate2;
                bool requestCharBeltUpdate2;
                bool requestCharCharUpdate2;

                bool acceptCharBagUpdate1;
                bool acceptCharBeltUpdate1;
                bool acceptCharCharUpdate1;

                bool acceptCharBagUpdate2;
                bool acceptCharBeltUpdate2;
                bool acceptCharCharUpdate2;

                RequestCharacter.RemoveFromInventory(RequesterTradeItems, out requestCharBagUpdate1, out requestCharBeltUpdate1, out requestCharCharUpdate1);
                RequestCharacter.AddToInventory(AcceptingTradeItems, out requestCharBagUpdate2, out requestCharBeltUpdate2, out requestCharCharUpdate2);

                AcceptingCharacter.RemoveFromInventory(AcceptingTradeItems, out acceptCharBagUpdate1, out acceptCharBeltUpdate1, out acceptCharCharUpdate1);
                AcceptingCharacter.AddToInventory(RequesterTradeItems, out acceptCharBagUpdate2, out acceptCharBeltUpdate2, out acceptCharCharUpdate2);

                if (AcceptingCoins != 0)
                {
                    RequestCharacter.Coins = RequestCharacter.Coins + AcceptingCoins;
                    AcceptingCharacter.Coins = AcceptingCharacter.Coins - AcceptingCoins;

                    requestCharCharUpdate1 = true;
                    acceptCharCharUpdate1 = true;
                }
                
                if (RequesterCoins != 0)
                {
                    AcceptingCharacter.Coins = AcceptingCharacter.Coins + RequesterCoins;
                    RequestCharacter.Coins = RequestCharacter.Coins - RequesterCoins;

                    requestCharCharUpdate1 = true;
                    acceptCharCharUpdate1 = true;
                }

                if (requestCharCharUpdate1 || requestCharCharUpdate2)
                    RequestCharacter.Update_Character();
                if (requestCharBagUpdate1 || requestCharBagUpdate2)
                    RequestCharacter.bagManager.UpdateBag();
                if (requestCharBeltUpdate1 || requestCharBeltUpdate2)
                    RequestCharacter.beltManager.UpdateBelt();

                if (acceptCharCharUpdate1 || acceptCharCharUpdate2)
                    AcceptingCharacter.Update_Character();
                if (acceptCharBagUpdate1 || acceptCharBagUpdate2)
                    AcceptingCharacter.bagManager.UpdateBag();
                if (acceptCharBeltUpdate1 || acceptCharBeltUpdate2)
                    AcceptingCharacter.beltManager.UpdateBelt();

                RequestCharacter.updateWeight();
                AcceptingCharacter.updateWeight();

                if (requestCharCharUpdate1 || requestCharCharUpdate2 ||
                    acceptCharCharUpdate1 || acceptCharCharUpdate2)
                {
                    LockItem lockitem = Managers.GameHandler.GetLockItem(AcceptingCharacter.MapID, AcceptingCharacter.Instance);
                    lock (lockitem.LockObj)
                    {
                        Map map = Managers.GameHandler.GetMap(AcceptingCharacter.MapID, AcceptingCharacter.Instance);
                        foreach (MapItem mapitem in map.MapItems)
                        {
                            if (Math.Abs(mapitem.MapX - AcceptingCharacter.MapX) <= 4 && Math.Abs(mapitem.MapY - AcceptingCharacter.MapY) <= 4)
                            {
                                foreach (CreatureBase cb in mapitem.CreaturesBases)
                                {
                                    if (cb is Character)
                                    {
                                        Character charToUpdate = (Character)cb;
                                        if (requestCharCharUpdate1 || requestCharCharUpdate2)
                                            charToUpdate.AddCreatureBase(RequestCharacter);
                                        if (acceptCharCharUpdate1 || acceptCharCharUpdate2)
                                            charToUpdate.AddCreatureBase(AcceptingCharacter);
                                    }
                                }
                            }
                        }
                    }
                }

            }
        }

        public void ChangeCoins(Character character, int numCoins)
        {
            lock (this)
            {
                if (character == AcceptingCharacter)
                {
                    if (AcceptingCoins != numCoins)
                    {
                        AcceptingCoins = numCoins;

                        AcceptingHasAccepted = false;
                        RequesterHasAccepted = false;

                        sendTradeData(RequestCharacter);
                    }
                }
                else if (character == RequestCharacter)
                {
                    if (RequesterCoins != numCoins)
                    {
                        RequesterCoins = numCoins;

                        AcceptingHasAccepted = false;
                        RequesterHasAccepted = false;

                        sendTradeData(AcceptingCharacter);
                    }
                }
                else if (character != AcceptingCharacter && character != RequestCharacter)
                    return;
            }
        }

        public void RemoveItem(Character character, long itemGUID)
        {
            lock (this)
            {
                if (character == AcceptingCharacter)
                {
                    foreach (Loot loot in AcceptingTradeItems)
                    {
                        if (loot.GameUID == itemGUID)
                        {
                            AcceptingTradeItems.Remove(loot);

                            AcceptingHasAccepted = false;
                            RequesterHasAccepted = false;
                            sendTradeData(RequestCharacter);
                            break;
                        }
                    }
                }
                else if (character == RequestCharacter)
                {
                    foreach (Loot loot in RequesterTradeItems)
                    {
                        if (loot.GameUID == itemGUID)
                        {
                            RequesterTradeItems.Remove(loot);

                            AcceptingHasAccepted = false;
                            RequesterHasAccepted = false;
                            sendTradeData(AcceptingCharacter);
                            break;
                        }
                    }
                }
            }
        }

        public void AddItem(Character character, long itemGUID)
        {
            lock (this)
            {
                Loot itemToAdd = null;

                //Check Bag
                if (itemToAdd == null)
                {
                    itemToAdd = character.bagManager.getBagItem(itemGUID);
                    if (itemToAdd != null)
                    {
                        if (character == AcceptingCharacter)
                        {
                            if (this.AcceptingTradeItems.Contains(itemToAdd))
                                return;
                        }
                        else if (character == RequestCharacter)
                        {
                            if (this.RequesterTradeItems.Contains(itemToAdd))
                                return;
                        }
                        else
                            return;
                    }
                }
                
                //Check Belt
                if (itemToAdd == null)
                {
                    itemToAdd = character.beltManager.GetBeltItem(itemGUID);
                    if (itemToAdd != null)
                    {
                        if (character == AcceptingCharacter)
                        {
                            if (this.AcceptingTradeItems.Contains(itemToAdd))
                                return;
                        }
                        else if (character == RequestCharacter)
                        {
                            if (this.RequesterTradeItems.Contains(itemToAdd))
                                return;
                        }
                        else
                            return;
                    }
                }

                //Check Left Hand
                if (itemToAdd == null)
                {
                    Loot leftHandItem = character.LeftHand;
                    if (leftHandItem != null && leftHandItem.GameUID == itemGUID)
                    {
                        if (character == AcceptingCharacter && AcceptingTradeItems.Contains(leftHandItem))
                            return;
                        else if (character == RequestCharacter && RequesterTradeItems.Contains(leftHandItem))
                            return;

                        itemToAdd = character.LeftHand;
                    }
                }

                //Check Right Hand
                if (itemToAdd == null)
                {
                    Loot rightHandItem = character.RightHand;
                    if (rightHandItem != null && rightHandItem.GameUID == itemGUID)
                    {
                        if (character == AcceptingCharacter && AcceptingTradeItems.Contains(rightHandItem))
                            return;
                        else if (character == RequestCharacter && RequesterTradeItems.Contains(rightHandItem))
                            return;

                        itemToAdd = character.RightHand;
                    }
                }

                if (itemToAdd != null)
                {
                    if (character == RequestCharacter)
                    {
                        RequesterTradeItems.Add(itemToAdd);

                        AcceptingHasAccepted = false;
                        RequesterHasAccepted = false;

                        sendTradeData(AcceptingCharacter);
                    }
                    else if (character == AcceptingCharacter)
                    {
                        AcceptingTradeItems.Add(itemToAdd);

                        AcceptingHasAccepted = false;
                        RequesterHasAccepted = false;

                        sendTradeData(RequestCharacter);
                    }
                    else
                        return;
                }
            }
        }
        private void openTradeWindow(Character toCharacter)
        {
            string result = "";

            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("opentradewindow");

            List<Loot> MyItems = new List<Loot>();
            if (toCharacter == RequestCharacter)
            {
                xmldoc.WriteAttributeString("TraderName", AcceptingCharacter.Name);
                xmldoc.WriteAttributeString("TraderCreatureID", AcceptingCharacter.CreatureID.ToString());
            }
            else if (toCharacter == AcceptingCharacter)
            {
                xmldoc.WriteAttributeString("TraderName", RequestCharacter.Name);
                xmldoc.WriteAttributeString("TraderCreatureID", RequestCharacter.CreatureID.ToString());
            }
            else if (toCharacter != AcceptingCharacter && toCharacter != RequestCharacter)
                return;

            //Send all of characters tradeable items
            xmldoc.WriteStartElement("MyItems");
            if (toCharacter.LeftHand != null)
                xmldoc.WriteRaw(toCharacter.LeftHand.toXML(toCharacter));
            if (toCharacter.RightHand != null)
                xmldoc.WriteRaw(toCharacter.RightHand.toXML(toCharacter));

            Loot[] beltLoot = toCharacter.beltManager.getBeltLootCopy();
            foreach (Loot loot in beltLoot)
            {
                if (loot != null)
                    xmldoc.WriteRaw(loot.toXML(toCharacter));
            }

            List<Loot> bagLoot = toCharacter.bagManager.getAllBagItems();
            foreach (Loot loot in bagLoot)
                xmldoc.WriteRaw(loot.toXML(toCharacter));
            
            xmldoc.WriteEndElement();
            //End tradeable items

            xmldoc.WriteEndElement();

            xmldoc.Flush();
            xmldoc.Close();
            stringWriter.Flush();
            result = stringWriter.ToString();

            try
            {
                toCharacter.ClientSocket.socket.BeginSend(result);
            }
            catch { }
        }
        private void sendTradeData(Character toCharacter)
        {
            string result = "";

            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("tradedata");

            if (toCharacter == RequestCharacter)
            {
                xmldoc.WriteAttributeString("Coins", AcceptingCoins.ToString());

                xmldoc.WriteStartElement("Offers");
                foreach (Loot loot in AcceptingTradeItems)
                {
                    xmldoc.WriteRaw(loot.toXML(toCharacter));
                }
                xmldoc.WriteEndElement();
            }
            else if (toCharacter == AcceptingCharacter)
            {
                xmldoc.WriteAttributeString("Coins", RequesterCoins.ToString());

                xmldoc.WriteStartElement("Offers");
                foreach (Loot loot in RequesterTradeItems)
                {
                    xmldoc.WriteRaw(loot.toXML(toCharacter));
                }
                xmldoc.WriteEndElement();
            }
            else
                return;

            xmldoc.WriteEndElement();

            xmldoc.Flush();
            xmldoc.Close();
            stringWriter.Flush();
            result = stringWriter.ToString();

            try
            {
                toCharacter.ClientSocket.socket.BeginSend(result);
            }
            catch { }
        }
    }
}
